using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Nivel21.XEngine.Rendering.Interfaces;

namespace Nivel21.XEngine.Rendering.Objects
{
    /// <summary>
    /// This class represent a 3D model that can be drawn
    /// </summary>
    public class CustomShaderMesh : Entity, ILoadable, IRenderable
    {
        private string modelAsset = "";
        private string shaderAsset = "";
        private Shader meshShader;

        public Shader Shader
        {
            get { return this.meshShader; }
        }

        #region "Constructors"
        
        public CustomShaderMesh(string ModelAsset, string ShaderAsset)
        {
            this.modelAsset = ModelAsset;
            this.shaderAsset = ShaderAsset;
        }

        #endregion

        #region ILoadable Members

        /// <summary>
        /// Load the mesh contents: model and shader
        /// </summary>
        public void LoadGraphicsContent(GraphicsDevice myDevice, Microsoft.Xna.Framework.Content.ContentManager myLoader)
        {
            this.meshModel = myLoader.Load<Model>(modelAsset);
            this.meshShader = Engine.Instance.ShaderManager.LoadShader(shaderAsset, shaderAsset);

            //subcribe to OnShaderParameters to specify the parameters
            this.meshShader.OnSetShaderParameters +=new Shader.ShaderParameterMethod(meshShader_OnSetShaderParameters);
        }

        #endregion

        #region IRenderable Members

        public void Render(ref Matrix worldMatrix, GraphicsDevice myDevice)
        {
            myDevice.RenderState.AlphaBlendEnable = false;
            myDevice.RenderState.AlphaTestEnable = false;

            //the the shader parameters
            this.meshShader.InjectParameters(worldMatrix);

            // Now, we will loop through each mesh in the model (in case there 
            // are more than one present. 
            foreach (ModelMesh mesh in meshModel.Meshes)
            {
                // Set the index buffer on the device once per mesh 
                myDevice.Indices = mesh.IndexBuffer;

                // Start the actual effect from the shader returned above 
                this.meshShader.Effect.Begin();

                // Loop through each pass in the effect like we do elsewhere 
                foreach (EffectPass pass in this.meshShader.Effect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    // Each mesh is made of parts (grouped by texture, etc.) 
                    foreach (ModelMeshPart part in mesh.MeshParts)
                    {
                        // Change the device settings for each part to be rendered 
                        myDevice.VertexDeclaration = part.VertexDeclaration;
                        myDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);

                        // Make sure we use the texture for the current part also 
                        myDevice.Textures[0] = ((BasicEffect)part.Effect).Texture;

                        // Finally draw the actual triangles on the screen 
                        myDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                        part.BaseVertex,
                                                        0,
                                                        part.NumVertices,
                                                        part.StartIndex,
                                                        part.PrimitiveCount);
                    }

                    pass.End();
                }

                this.meshShader.Effect.End();

                // Pull out of all of the passes and the effect 
            }

        }

        #endregion

        #region "Shader Parameters"

        protected Vector4 diffuseColor = new Vector4(0.5f, 0.5f, 0.5f, 1.0f);

        /// <summary>
        /// Controls the specular power shader parameter 
        /// </summary>
        public Vector4 DiffuseColor
        {
            get { return diffuseColor; }
            set { diffuseColor = value; }
        }

        protected float shininess = 0.4f;
        
        /// <summary>
        /// Controls the specular power shader parameter 
        /// </summary>
        public float Shininess
        {
            get { return shininess; }
            set { shininess = value; }
        }

        protected float specularPower = 4.0f;

        /// <summary>
        /// Controls the specular power shader parameter 
        /// </summary>
        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }

        /// <summary>
        /// Set the shader effect parameters for this object
        /// </summary>
        void meshShader_OnSetShaderParameters(Effect shader)
        {
            if (shader.Parameters["SpecularPower"] != null)
                shader.Parameters["SpecularPower"].SetValue(specularPower);

            if (shader.Parameters["Shininess"] != null)
                shader.Parameters["Shininess"].SetValue(shininess);

            if (shader.Parameters["DiffuseColor"] != null)
                shader.Parameters["DiffuseColor"].SetValue(diffuseColor);
        }

        #endregion

    }

}


